package com.yalemang.framework.utils

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.viewbinding.ViewBinding
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType

/**
 * 用于Base层自动绑定ViewBinding的工具类
 * @author cl
 */
object ViewBindingUtils {
    private const val METHOD_NAME_INFLATE = "inflate"
    private const val METHOD_NAME_BIND_VIEW = "bind"

    /**
     * 通过反射方式解析泛型获取ViewBinding
     *
     * @param context 上下文对象
     * @param target 泛型目标对象
     * @return ViewBinding对象
     */
    fun <VB : ViewBinding> getViewBinding(target: Any, context: Context): VB {
        return getViewBinding(target, LayoutInflater.from(context))!!
    }

    /**
     * 通过反射方式解析泛型获取ViewBinding
     *
     * @param layoutInflater 布局解析器
     * @param target 泛型目标对象
     * @return ViewBinding对象
     */
    fun <VB : ViewBinding> getViewBinding(target: Any, layoutInflater: LayoutInflater): VB? {
        try {
            val vbClass = getViewBindingClass(target)
            var vbInflateMethod: Method? = null
            if (vbClass != null) {
                vbInflateMethod = vbClass
                    .getDeclaredMethod(METHOD_NAME_INFLATE, LayoutInflater::class.java)
            }

            if (vbInflateMethod != null) {
                vbInflateMethod.isAccessible = true
                val binding = vbInflateMethod.invoke(null, layoutInflater)
                return binding as VB
            }
        } catch (e: Exception) {
            // 异常情况
            throw RuntimeException(e)
        }
        return null
    }

    /**
     * 通过反射方式解析泛型获取ViewBinding
     *
     * @param layoutInflater 布局解析器
     * @param parent 父View对象
     * @param isAttachParent 是否绑定到父View
     * @param target 泛型目标对象
     * @return ViewBinding对象
     */
    fun <VB : ViewBinding> getViewBinding(
        target: Any,
        layoutInflater: LayoutInflater,
        parent: ViewGroup?,
        isAttachParent: Boolean
    ): VB? {
        try {
            val vbClass = getViewBindingClass(target)
            var vbInflateMethod: Method? = null
            if (vbClass != null) {
                vbInflateMethod = vbClass
                    .getDeclaredMethod(
                        METHOD_NAME_INFLATE,
                        LayoutInflater::class.java,
                        ViewGroup::class.java,
                        Boolean::class.javaPrimitiveType
                    )
            }

            if (vbInflateMethod != null) {
                vbInflateMethod.isAccessible = true
                val binding = vbInflateMethod.invoke(null, layoutInflater, parent, isAttachParent)
                return binding as VB
            }
        } catch (e: Exception) {
            // 异常情况
            throw RuntimeException(e)
        }
        return null
    }

    /**
     * 通过反射方式解析泛型获取ViewBinding
     *
     * @param rootView 根视图
     * @param target 泛型目标对象
     * @return ViewBinding对象
     */
    fun <VB : ViewBinding?> getViewBinding(target: Any, rootView: View): VB? {
        try {
            val vbClass = getViewBindingClass(target)
            var vbBindMethod: Method? = null
            if (vbClass != null) {
                vbBindMethod = vbClass
                    .getDeclaredMethod(METHOD_NAME_BIND_VIEW, LayoutInflater::class.java)
            }

            if (vbBindMethod != null) {
                vbBindMethod.isAccessible = true
                val binding = vbBindMethod.invoke(null, rootView)
                return binding as VB
            }
        } catch (e: Exception) {
            // 异常情况
            throw RuntimeException(e)
        }
        return null
    }

    /**
     * 获取泛型类中ViewBinding类型对应的字节码对象
     *
     * @param target 泛型目标对象
     * @return ViewBinding对应的字节码对象
     */
    private fun getViewBindingClass(target: Any): Class<*>? {
        val clazz: Class<*> = target.javaClass
        val parameterizedType = clazz.genericSuperclass as ParameterizedType
        val typeArray = parameterizedType.actualTypeArguments
        var currentClass: Class<*>
        var vbClass: Class<*>? = null
        for (type in typeArray) {
            currentClass = type as Class<*>
            // 判断是否继承于ViewBinding接口
            if (ViewBinding::class.java.isAssignableFrom(currentClass)) {
                vbClass = currentClass
                break
            }
        }
        return vbClass
    }
}